package ir2

import (
	"fmt"
	"strconv"
	"strings"
	"sync"

	"gitee.com/u-language/u-language/ucom/ast"
	"gitee.com/u-language/u-language/ucom/astdata"
	"gitee.com/u-language/u-language/ucom/enum"
	"gitee.com/u-language/u-language/ucom/internal/errutil"
	"gitee.com/u-language/u-language/ucom/internal/utils"
)

func NodeToIr(f *File, n []ast.Node) {
	for i := range n {
		OneNodeToIr(f, n[i], i+1, true)
	}
}

// OneNodeToIr 将一个抽象语法树节点转换为ir
func OneNodeToIr(f *File, n ast.Node, LineNum int, linefeed bool) {
	if utils.IsNil(n) {
		return
	}
	switch n := n.(type) {
	case *ast.VarNode:
		varToIr(f, n, LineNum, linefeed)
	//case ast.Const:
	//TODO:支持常量
	case *ast.ASSIGNNode:
		assignToIr(f, n, LineNum, linefeed)
	case *ast.OpExpr:
		opToIr(f, n, LineNum, linefeed)
	case *ast.FuncNode:
		ir := NewIr(FuncOP, LineNum)
		fn := ast.Node(n)
		*ir.Func(), f.Sbt = &fn, n.Sbt
		f.Ir = append(f.Ir, ir)
		f.GlobalDecl = append(f.GlobalDecl, n)
	case *ast.MethodNode:
		ir := NewIr(FuncOP, LineNum)
		fn := ast.Node(n)
		*ir.Func(), f.Sbt = &fn, n.Sbt
		f.Ir = append(f.Ir, ir)
		f.GlobalDecl = append(f.GlobalDecl, n)
	case ast.RbraceNode:
		f.Ir = append(f.Ir, NewIr(RbraceOP, LineNum))
	case *ast.CallNode:
		callToIr(f, n, LineNum, true)
	case *ast.ReturnNode:
		retToIr(f, n, LineNum)
	case *ast.ForNode:
		f.Sbt = n.Sbt
		forToIr(f, n, LineNum)
	case *ast.IfNode:
		f.Sbt = n.Sbt
		ifToIr(f, n, LineNum)
	case *ast.ElseNode:
		f.Sbt = n.Sbt
		elseToIr(f, n, LineNum)
	case *ast.StructDecl:
		if !n.InFunc {
			f.GlobalDecl = append(f.GlobalDecl, n)
			return
		}
		structToIr(f, n, LineNum)
	default:
		panic(errutil.NewErr2(errutil.CompileErr, fmt.Sprintf("未知的节点：%+v", n)))
	}
}

// varToIr 将一个变量节点转换为ir
func varToIr(f *File, v *ast.VarNode, LineNum int, linefeed bool) {
	ir := NewIr(VarOP, LineNum)
	*ir.Var() = v
	if !v.IsFunc { //如果不在函数内，就是全局变量
		f.Ir = append(f.Ir, ir)
		f.GlobalDecl = append(f.GlobalDecl, v)
		if v.Value != nil { //如果有初始化
			f.toS(&f.VarInit, func() {
				ir := NewIr(MOVOP, LineNum)
				ir.ResultObj, ir.Arg2Obj = v.Name, enum.StrFalse
				f.Ir = append(f.Ir, ir)
				oneExprToIr(f, v.Value, LineNum)
				f.Ir = append(f.Ir, NewIr(SemicolonOp, LineNum))
			})
		}
		return
	}
	ir.Op = LocalVarOp
	//TODO:复制TYPE避免引用变量节点，影响对抽象语法树的GC
	ir.Arg1Obj, *ir.Type() = v.Name, &v.TYPE
	f.Ir = append(f.Ir, ir)
	if v.Value != nil {
		oneExprToIr(f, v.Value, LineNum)
	}
	if linefeed {
		f.Ir = append(f.Ir, NewIr(SemicolonOp, LineNum))
		f.Ir = append(f.Ir, NewIr(LineFeedOp, LineNum))
	}
}

// assignToIr 将一个赋值节点转换为ir
func assignToIr(f *File, v *ast.ASSIGNNode, LineNum int, linefeed bool) {
	if dest, ok := v.Dest.(*ast.Object); ok {
		ir := NewIr(MOVOP, LineNum)
		ir.ResultObj, ir.ResultTyp = retObj(f, dest)
		if src, ok := v.Src.(*ast.Object); ok { //如果是obj=obj
			ir.Arg1Obj, ir.Arg1Typ = retObj(f, src)
		} else { //如果是obj=!obj
			ir.Arg2Obj = enum.StrFalse
			f.Ir = append(f.Ir, ir)
			oneExprToIr(f, v.Src, LineNum)
			if linefeed {
				f.Ir = append(f.Ir, NewIr(SemicolonOp, LineNum))
				f.Ir = append(f.Ir, NewIr(LineFeedOp, LineNum))
			}
		}
	} else {
		oneExprToIr(f, v.Dest, LineNum)
		f.Ir = append(f.Ir, NewIr(AssignOp, LineNum))
		oneExprToIr(f, v.Src, LineNum)
		if linefeed {
			f.Ir = append(f.Ir, NewIr(SemicolonOp, LineNum))
			f.Ir = append(f.Ir, NewIr(LineFeedOp, LineNum))
		}
	}
}

// opToIr 将一个运算节点转换为ir
func opToIr(f *File, v *ast.OpExpr, LineNum int, linefeed bool) {
	var op OPEnum
	switch v.OP {
	case enum.ADDOP:
		op = ADDOP
	case enum.SUBOP:
		op = SUBOP
	case enum.MULOP:
		op = MULOP
	case enum.DIVOP:
		op = DIVOP
	case enum.LessOP:
		op = LessOp
	case enum.GreaterOP:
		op = GreaterOp
	case enum.EqualOP:
		op = EqualOp
	case enum.NoEqualOP:
		op = NoEqualOp
	case enum.RemainOP:
		op = RemainOp
	default:
		panic(errutil.NewErr2(errutil.CompileErr, fmt.Sprintf("未知的运算 %v", v.OP)))
	}
	ir := NewIr(op, LineNum)
	if !linefeed { //如果要不换行
		ir.ResultTyp = enum.StrFalse
	}
	o1, src1 := v.Src1.(*ast.Object)
	o2, src2 := v.Src2.(*ast.Object)
	if src1 && src2 { //如果是两个对象表达式
		ir.Arg1Obj, ir.Arg1Typ = retObj(f, o1)
		ir.Arg2Obj, ir.Arg2Typ = retObj(f, o2)
	} else {
		oneExprToIr(f, v.Src1, LineNum)
		f.Ir = append(f.Ir, ir)
		oneExprToIr(f, v.Src2, LineNum)
		return
	}
	f.Ir = append(f.Ir, ir)
}

// retToIr 将一个返回节点转换为ir
func retToIr(f *File, v *ast.ReturnNode, LineNum int) {
	ir := NewIr(RetOp, LineNum)
	if v.RetValue != nil { //如果有返回值
		if o, ok := v.RetValue.(*ast.Object); ok {
			ir.Arg1Obj, ir.Arg1Typ = retObj(f, o)
			ir.Arg2Obj = enum.StrFalse
			f.Ir = append(f.Ir, ir)
		} else {
			f.Ir = append(f.Ir, ir)
			ret := &f.Ir[len(f.Ir)-1]
			oneExprToIr(f, v.RetValue, LineNum)
			ret.ResultObj = strconv.Itoa(len(f.Ir) - 1)
			f.Ir = append(f.Ir, NewIr(SemicolonOp, LineNum))
			f.Ir = append(f.Ir, NewIr(LineFeedOp, LineNum))
		}
	} else { //如果没有返回值
		f.Ir = append(f.Ir, NewIr(SemicolonOp, LineNum))
	}
}

// callToIr 将一个调用节点转换为ir
func callToIr(f *File, v *ast.CallNode, LineNum int, linefeed bool) {
	c := NewIr(CallOP, LineNum)
	switch s := v.FuncName.FuncName(); s {
	case enum.Int, enum.Float:
		c.Op = TypeConvertOp
		c.Arg1Obj = s
		//TODO:支持非obj的类型准换
		c.Arg2Obj, c.Arg2Typ = retObj(f, v.Parame[0].(*ast.Object))
		f.Ir = append(f.Ir, c)
		return
	case enum.Malloc:
		c.Op = Malloc
		(*c.Arg1ObjExpr()) = &v.Parame[0]
		if !linefeed {
			c.ResultTyp = enum.StrFalse
		}
		f.Ir = append(f.Ir, c)
		return
	}
	c.Arg1Obj = generateCallName(v, f.Sbt)
	(*c.FuncName()) = &v.FuncName
	if len(v.Parame) == 0 { //如果没有传参
		c.ResultObj = enum.StrFalse
		if !linefeed {
			c.ResultTyp = enum.StrFalse
		}
		f.Ir = append(f.Ir, c) //记录调用
		return
	}
	c.ResultObj = strconv.Itoa(len(v.Parame)) //记录传参数量
	f.Ir = append(f.Ir, c)                    //记录调用
	if !linefeed {
		c.ResultTyp = enum.StrFalse
	}
	for i, p := range v.Parame { //将传递参数转换为ir
		oneExprToIr(f, p, LineNum)
		if i != len(v.Parame)-1 { //如果不是最后一个传参
			f.Ir = append(f.Ir, NewIr(CommaOp, LineNum))
		}
	}
	c = NewIr(EndCallOp, LineNum)
	if !linefeed {
		c.ResultTyp = enum.StrFalse
	}
	f.Ir = append(f.Ir, c)
}

var sliceP = &sync.Pool{
	New: func() interface{} {
		ret := make([]IrNode, 0, 1)
		return &ret
	},
}

func putIr(s *[]IrNode) {
	(*s) = (*s)[0:0]
	sliceP.Put(s)
}

// generateCallName 生成被调用的函数名
func generateCallName(c *ast.CallNode, sbt *ast.Sbt) string {
	var funcname string
	if n, ok := c.FuncName.(*ast.Objects); ok {
		info := sbt.Have(c.Parame[0].(*ast.Object).Name)
		if info.Kind == enum.SymbolMethod { //如果是方法
			n := info.Info.(*ast.MethodNode)
			return astdata.Generate_method_symbol(n.Typ, n.FileName)
		} else {
			var buf strings.Builder
			buf.WriteString(n.Slice[0].Name)
			for _, v := range n.Slice[1:] {
				buf.WriteString(enum.PackageSep)
				buf.WriteString(v.Name)
			}
			funcname = buf.String()
		}
	} else {
		funcname = c.FuncName.FuncName()
	}
	return funcname
}

// forToIr 将一个for节点转换为ir
func forToIr(f *File, v *ast.ForNode, LineNum int) {
	ir := NewIr(ForOp, LineNum)
	f.Ir = append(f.Ir, ir)
	OneNodeToIr(f, v.InitStmt, LineNum, false) //转换初始化语句
	f.Ir = append(f.Ir, NewIr(SemicolonOp, LineNum))
	oneExprToIr(f, v.BoolExpr, LineNum) //转换布尔表达式
	f.Ir = append(f.Ir, NewIr(SemicolonOp, LineNum))
	OneNodeToIr(f, v.EndStmt, LineNum, false) //转换结束语句
	f.Ir = append(f.Ir, NewIr(RPARENOp, LineNum))
	f.Ir = append(f.Ir, NewIr(LbraceOp, LineNum))
	f.Ir = append(f.Ir, NewIr(LineFeedOp, LineNum))
}

// oneExprToIr 将一个抽象语法树的表达式节点转换为ir
func oneExprToIr(f *File, n ast.Expr, LineNum int) {
	if utils.IsNil(n) {
		return
	}
	switch t := n.(type) {
	case *ast.OpExpr:
		opToIr(f, t, LineNum, false)
	case *ast.CallNode:
		callToIr(f, t, LineNum, false)
	case *ast.Object:
		objToIr(f, t, LineNum)
	case *ast.Objects:
		selectToIr(f, t, LineNum)
	default:
		panic(errutil.NewErr2(errutil.CompileErr, fmt.Sprintf("未知的节点：%+v", n)))
	}
}

func objToIr(f *File, obj *ast.Object, LineNum int) {
	ir := NewIr(ObjOP, LineNum)
	ir.ResultObj, ir.ResultTyp = retObj(f, obj)
	f.Ir = append(f.Ir, ir)
}

func retObj(f *File, obj *ast.Object) (name string, typ string) {
	name = obj.Name
	switch obj.Kind {
	case ast.LeaObj:
		name = "&" + name
	case ast.DerefObj:
		name = fmt.Sprint("(*", name, ")")
	case ast.NilObj:
		name = "NULL"
	}
	typ = ast.RetType(obj, f.Sbt)
	return
}

// ifToIr 将一个if节点转换为ir
func ifToIr(f *File, v *ast.IfNode, LineNum int) {
	ir := NewIr(IfOp, LineNum)
	//Note:ast保证布尔表达式不为nil
	boolToIr(f, &ir, v.BoolExpr, LineNum)
}

func boolToIr(f *File, ir *IrNode, b ast.Expr, LineNum int) {
	if o, ok := b.(*ast.Object); ok {
		ir.Arg1Obj, ir.Arg1Typ = retObj(f, o)
		ir.Arg2Obj = enum.StrFalse
		f.Ir = append(f.Ir, *ir)
	} else {
		f.Ir = append(f.Ir, *ir)
		oneExprToIr(f, b, LineNum)
		f.Ir = append(f.Ir, NewIr(RPARENOp, LineNum))
		f.Ir = append(f.Ir, NewIr(LbraceOp, LineNum))
		f.Ir = append(f.Ir, NewIr(LineFeedOp, LineNum))
	}
}

// elseToIr 将一个else节点转换为ir
func elseToIr(f *File, v *ast.ElseNode, LineNum int) {
	ir := NewIr(ElseOp, LineNum)
	if v.BoolExpr == nil { //如果是else
		f.Ir = append(f.Ir, ir)
		return
	}
	ir.Op = ElseIfOp
	//如果是else if
	boolToIr(f, &ir, v.BoolExpr, LineNum)
}

// structToIr 将一个struct节点转换为ir
func structToIr(f *File, v *ast.StructDecl, LineNum int) {
	ir := NewIr(StructDeclStart, LineNum)
	l := len(v.FieldTable)
	defer func() {
		ir.Op = StructDeclEnd
		ir.ResultObj = v.Name
		f.Ir = append(f.Ir, ir)
	}()
	//优化有1个或2个或3个字段的结构体
	switch l {
	case 1:
		ir.Op = Struct1Field
		ir.Arg1Obj = v.FieldTable[0].Name
		*ir.Arg1ObjTyp() = &v.FieldTable[0].Type
		f.Ir = append(f.Ir, ir)
		return
	case 2:
		ir.Op = Struct2Field
		ir.Arg1Obj = v.FieldTable[0].Name
		*ir.Arg1ObjTyp() = &v.FieldTable[0].Type
		ir.Arg2Obj = v.FieldTable[1].Name
		*ir.Arg2ObjTyp() = &v.FieldTable[1].Type
		f.Ir = append(f.Ir, ir)
		return
	case 3:
		f.Ir = append(f.Ir, ir)
		ir.Op = Field3
		ir.Arg1Obj = v.FieldTable[0].Name
		*ir.Arg1ObjTyp() = &v.FieldTable[0].Type
		ir.Arg2Obj = v.FieldTable[1].Name
		*ir.Arg2ObjTyp() = &v.FieldTable[1].Type
		ir.ResultObj = v.FieldTable[2].Name
		*ir.ResultObjTyp() = &v.FieldTable[2].Type
		f.Ir = append(f.Ir, ir)
		return
	}
	//处理大于3个字段的结构体
	f.Ir = append(f.Ir, ir)
	s := v.FieldTable
	ir.Op = Field
	for len(s)%3 != 0 {
		ir.Arg1Obj = s[0].Name
		*ir.Arg1ObjTyp() = &s[0].Type
		s = s[1:]
		f.Ir = append(f.Ir, ir)
	}
	ir.Op = Field3
	for len(s) >= 3 {
		ir.Arg1Obj = s[0].Name
		*ir.Arg1ObjTyp() = &s[0].Type
		ir.Arg2Obj = s[1].Name
		*ir.Arg2ObjTyp() = &s[1].Type
		ir.ResultObj = s[2].Name
		*ir.ResultObjTyp() = &s[2].Type
		s = s[3:]
		f.Ir = append(f.Ir, ir)
	}
}

// selectToIr 将一个选择器节点转换为ir
func selectToIr(f *File, v *ast.Objects, LineNum int) {
	ir := NewIr(LeftSelectRight, LineNum)
	s := v.Slice
	ir.Arg1Obj = s[0].Name
	ir.Arg2Obj = s[1].Name
	f.Ir = append(f.Ir, ir)
	if len(s) == 2 {
		return
	}
	s = s[2:]
	ir.Op = SelectRight
	for len(s) != 0 {
		ir.Arg1Obj = s[0].Name
		f.Ir = append(f.Ir, ir)
		s = s[1:]
	}
}
